package com.lxf.cn.redis;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Created by liaoxuefeng on 2018/11/6.
 */

@Component
public class RedisUitls {

    private final Charset UTF_8 = Charset.forName("utf-8");

    public static final int hour_24 = 24 * 60 * 60;
    public static final int hour_12 = 12 * 60 * 60;
    public static final int hour_6 = 6 * 60 * 60;
    public static final int hour_1 = 60 * 60;


    @Autowired
    RedisConfiguration redisTool;

    /**
     * 存入并设置生命周期
     *
     * @param key
     * @param seconds
     * @param value
     * @return
     */
    public String setValueAndLifeCycle(String key, int seconds, String value) {
        Jedis jedis = redisTool.getJedis();
        String result = jedis.setex(key, seconds, value);
        jedis.close();
        return result;
    }

    /**
     * 向redis新增字符串键值对
     *
     * @param key
     * @param value
     */
    public boolean setString(String key, String value) {
        if (null == key || value == null) {
            return false;
        }

        return setBytes(key.getBytes(UTF_8), value.getBytes(UTF_8));
    }

    /**
     * 向Redis中储存键值对的byte数组，最长不能超过1GB的字节
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean setBytes(byte[] key, byte[] value) {
        if (null == key || null == value) {
            return false;
        }

        Jedis jedis = redisTool.getJedis();
        String statusCode = jedis.set(key, value);
        System.out.println("状态码:(" + statusCode + ")");
        redisTool.returnResource(jedis);
        return true;
    }

    /**
     * 获取String类型的值
     *
     * @param key 键的值
     * @return
     */
    public String getString(String key) {
        if (null == key) {
            return null;
        }

        byte[] val = getBytes(key.getBytes(UTF_8));
        if (val == null) {
            return null;
        }
        return new String(val, UTF_8);
    }

    /**
     * 获取Redis中的缓存值
     *
     * @param key
     * @return
     */
    public byte[] getBytes(byte[] key) {
        if (null == key) {
            return null;
        }

        Jedis jedis = redisTool.getJedis();
        byte[] val = jedis.get(key);
        redisTool.returnResource(jedis);
        return val;
    }

    /**
     * 删除某个键，如果键被删除，再次请求相同键时，返回null
     *
     * @param key
     */
    private boolean del(byte[] key) {
        if (null == key) {
            return true;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.del(key);
        return true;
    }

    /**
     * 操作字符串类型(String),删除键
     *
     * @param key
     * @return
     */
    public boolean delString(String key) {
        if (null == key) {
            return true;
        }

        byte[] k = key.getBytes(UTF_8);
        return del(k);
    }

    /**
     * 批量插入缓存:<br>
     * key,value,key,value<br>
     * 例如<br>
     * name，johnny,age,12<br>
     * 则会新增name=johnny,age=12的缓存，如果在缓存中已经存在相同的缓存，则会立即更新。
     *
     * @param keyValues
     * @return
     */
    public boolean fetchSet(String... keyValues) {
        if (keyValues == null) {
            return false;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.mset(keyValues);
        redisTool.returnResource(jedis);
        return true;
    }

    /**
     * 插入一个简单类型的Map
     *
     * @param key
     * @param map
     */
    public void addMap(String key, Map<String, String> map) {
        if (null == key || null == map) {
            return;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.hmset(key, map);
        redisTool.returnResource(jedis);
    }

    public void addMapVal(String key, String field, String value) {
        if (null == key || field == null || null == value) {
            return;
        }
        Jedis jedis = redisTool.getJedis();
        jedis.hsetnx(key, field, value);
        redisTool.returnResource(jedis);
    }

    public void addMapVal(byte[] key, byte[] field, byte[] value) {
        if (null == key || field == null || null == value) {
            return;
        }
        Jedis jedis = redisTool.getJedis();
        jedis.hsetnx(key, field, value);
        redisTool.returnResource(jedis);
    }

    /**
     * 向Redis中插入一个Map的值
     *
     * @param key
     * @param mapByte
     */
    public void addMap(byte[] key, Map<byte[], byte[]> mapByte) {
        if (null == key || null == mapByte) {
            return;
        }

        Jedis jedis = redisTool.getJedis();
        //总是会返回OK,并不会执行失败
        String status = jedis.hmset(key, mapByte);
        System.out.println("执行状态:" + status);
        redisTool.returnResource(jedis);
    }

    /**
     * 获取Map中的值，只能够
     *
     * @param key
     * @return
     */
    public List<String> getMapVal(String key, String... fields) {
        if (null == key) {
            return null;
        }

        Jedis jedis = redisTool.getJedis();

        List<String> rtnList = null;
        if (null == fields || fields.length == 0) {
            rtnList = jedis.hvals(key);
        } else {
            rtnList = jedis.hmget(key, fields);
        }

        redisTool.returnResource(jedis);
        return rtnList;
    }

    /**
     * 获取Map中的值
     *
     * @param key
     * @param fields
     * @return
     */
    public List<byte[]> getMapVal(byte[] key, byte[]... fields) {
        if (null == key) {
            return null;
        }
        Jedis jedis = redisTool.getJedis();

        if (!jedis.exists(key)) {
            return null;
        }
        List<byte[]> rtnList = null;
        if (null == fields || fields.length == 0) {
            rtnList = jedis.hvals(key);
        } else {
            rtnList = jedis.hmget(key, fields);
        }

        return rtnList;
    }

    /**
     * 向Redis中添加set集合
     *
     * @param key
     * @param values
     */
    public void addSet(String key, String... values) {
        if (null == key || values == null) {
            return;
        }
        Jedis jedis = redisTool.getJedis();
        jedis.sadd(key, values);
    }

    public void delSetVal(String key, String... fields) {
        if (null == key) {
            return;
        }

        if (fields == null || fields.length == 0) {
            del(key.getBytes(UTF_8));
            return;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.srem(key, fields);
        redisTool.returnResource(jedis);
    }

    public void addSetBytes(byte[] key, byte[]... values) {
        if (null == key || values == null) {
            return;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.sadd(key, values);
        redisTool.returnResource(jedis);
    }

    public void delSetVal(byte[] key, byte[]... values) {
        if (null == key) {
            return;
        }
        if (values == null || values.length == 0) {
            del(key);
            return;
        }
        Jedis jedis = redisTool.getJedis();
        jedis.srem(key, values);
        redisTool.returnResource(jedis);
    }

    /**
     * 获取所有的值
     *
     * @param key
     */
    public Set<byte[]> getSetVals(byte[] key) {
        if (null == key) {
            return null;
        }
        Jedis jedis = redisTool.getJedis();
        Set<byte[]> rtnList = jedis.smembers(key);
        return rtnList;
    }

    public Set<String> getSetVals(String key) {
        if (null == key) {
            return null;
        }
        Jedis jedis = redisTool.getJedis();
        Set<String> rtnSet = jedis.smembers(key);
        redisTool.returnResource(jedis);
        return rtnSet;
    }

    /**
     * 判断是否Set集合中包含元素
     *
     * @param key
     * @param field
     * @return
     */
    public boolean isSetContain(String key, String field) {
        if (null == key || field == null) {
            return false;
        }
        Jedis jedis = redisTool.getJedis();
        boolean isContain = jedis.sismember(key, field);
        redisTool.returnResource(jedis);
        return isContain;
    }

    public boolean isSetContain(byte[] key, byte[] field) {
        if (null == key || field == null) {
            return false;
        }
        Jedis jedis = redisTool.getJedis();
        boolean isSuccess = jedis.sismember(key, field);
        redisTool.returnResource(jedis);
        return isSuccess;
    }

    /**
     * 返回Set集合中的元素个数
     *
     * @param key
     * @return
     */
    public Long getSetLength(String key) {
        if (null == key) {
            return 0L;
        }
        Jedis jedis = redisTool.getJedis();
        Long length = jedis.scard(key);
        return length;
    }

    public Long getSetLength(byte[] key) {
        if (null == key) {
            return 0L;
        }
        Jedis jedis = redisTool.getJedis();
        Long length = jedis.scard(key);
        redisTool.returnResource(jedis);
        return length;
    }

    /**
     * 向list集合中添加元素
     *
     * @param key
     * @param values
     */
    public void addList(String key, String... values) {
        if (null == key || values == null) {
            return;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.rpush(key, values);
        redisTool.returnResource(jedis);
    }

    /**
     * 向list集合中添加元素
     *
     * @param key
     * @param values
     */
    public void addList(byte[] key, byte[]... values) {
        if (null == key || values == null) {
            return;
        }

        Jedis jedis = redisTool.getJedis();
        jedis.rpush(key, values);
        redisTool.returnResource(jedis);
    }

    /**
     * 获取start到end范围的值，超出list的范围，不会抛出异常
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<String> getListVals(String key, int start, int end) {
        if (null == key) {
            return null;
        }

        Jedis jedis = redisTool.getJedis();
        List<String> rtnList = jedis.lrange(key, start, end);
        redisTool.returnResource(jedis);
        return rtnList;
    }

    /**
     * 获取start到end范围的值，超出list的范围，不会抛出异常
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<byte[]> getListVals(byte[] key, int start, int end) {
        if (null == key) {
            return null;
        }

        Jedis jedis = redisTool.getJedis();
        List<byte[]> rtnList = jedis.lrange(key, start, end);
        redisTool.returnResource(jedis);
        return rtnList;
    }

    public List<String> getListAll(String key) {
        if (null == key) {
            return null;
        }
        return getListVals(key, 0, -1);
    }

    public List<byte[]> getListAll(byte[] key) {
        if (null == key) {
            return null;
        }
        return getListVals(key, 0, -1);
    }

    public String popList(String key) {
        if (null == key) {
            return null;
        }
        Jedis jedis = redisTool.getJedis();
        return jedis.lpop(key);
    }

    public byte[] popList(byte[] key) {
        if (null == key) {
            return null;
        }
        Jedis jedis = redisTool.getJedis();
        return jedis.lpop(key);
    }



    /**
     * 加锁
     * @param lockName       锁的key
     * @param acquireTimeout 获取超时时间
     * @param timeout        锁的超时时间
     * @return 锁标识
     */
    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 获取连接
            conn =  redisTool.getJedis() ;
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();
            // 锁名，即key值
            String lockKey = "lock:" + lockName;
            // 超时时间，上锁后超过此时间则自动释放锁
            int lockExpire = (int) (timeout / 1000);

            // 获取锁的超时时间，超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    // 返回value值，用于释放锁时间确认
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // 返回-1代表key没有设置超时时间，为key设置一个超时时间
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        //返还资源
        redisTool.returnResource(conn);
        return retIdentifier;
    }

    /**
     * 释放锁
     * @param lockName   锁的key
     * @param identifier 释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn =  redisTool.getJedis();
            while (true) {
                // 监视lock，准备开始事务
                conn.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁，若是该锁，则删除，释放锁
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        //返还资源
        redisTool.returnResource(conn);
        return retFlag;
    }




}
